﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Management;
using System.Management.Instrumentation;
using NLog;
using System.Threading;
using System.Threading.Tasks;

namespace ServerMonitor
{
    public class WMIService
    {
        private ManagementScope ms;

        private bool localConnection = false;

        private string ip;

        private string userName;

        private string password;

        private static int maxRetryTime = 2;

        private int connectionStatus = 0; //0为恢复起始状态，1为连接中

        private int processStatus = 0; //0为初始状态，1为处理中

        private static ILogger logger = LogManager.GetCurrentClassLogger();

        //定义AggregateException 的事件委托
        private event EventHandler<AggregateExceptionArgs> AggregateExceptionCatched = new EventHandler<AggregateExceptionArgs>(Program_AggregateExceptionCatched);


        public ManagementScope Ms
        {
            get
            {
                return ms;
            }

            set
            {
                ms = value;
            }
        }

        public string Ip
        {
            get
            {
                return ip;
            }

            set
            {
                ip = value;
            }
        }

        public string UserName
        {
            get
            {
                return userName;
            }

            set
            {
                userName = value;
            }
        }

        public string Password
        {
            get
            {
                return password;
            }

            set
            {
                password = value;
            }
        }

        public bool LocalConnection
        {
            get
            {
                return localConnection;
            }

            set
            {
                localConnection = value;
            }
        }

        public int ConnectionStatus
        {
            get
            {
                return connectionStatus;
            }

            set
            {
                connectionStatus = value;
            }
        }

        public int ProcessStatus
        {
            get
            {
                return processStatus;
            }

            set
            {
                processStatus = value;
            }
        }

        public WMIService()
        {
            Ms = new ManagementScope();
        }

        public WMIService(string ip, string userName, string password)
        {
            Ip = ip;
            UserName = userName;
            Password = password;
            ConnectionOptions Conn = new ConnectionOptions();
            if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password) || "127.0.0.1" == ip)
            {
                //do nothing
                localConnection = true;
                logger.Info("empty username or pwd.server ip:{0}", ip);
            }
            else {
                Conn.Username = userName;
                Conn.Password = password;
                Conn.Impersonation = ImpersonationLevel.Impersonate;
                Conn.Authentication = AuthenticationLevel.PacketPrivacy;
                Conn.EnablePrivileges = true;
            }
            Ms = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", ip), Conn);
        }

        public void connect()
        {
            if (localConnection || connectionStatus ==1) return;//当前连接为本地连接或者处在连接中时不进行额外连接发起操作
            //AggregateExceptionCatched = new EventHandler<AggregateExceptionArgs>(Program_AggregateExceptionCatched);
            Task t1 = new Task(() =>
            {
                try
                {
                    connectionStatus = 1; //将状态置为正在连接中
                    ms.Connect();
                    connectionStatus = 0; //连接成功后恢复初始状态
                }
                catch (Exception ex)
                {
                    connectionStatus = 0; //连接失败后恢复初始状态
                    AggregateExceptionArgs args = new AggregateExceptionArgs()
                    {
                        AggregateException = new AggregateException(ex)
                    };
                    //使用主线程委托代理，处理子线程异常
                    //这种方式没有阻塞 主线程或其他线程
                    AggregateExceptionCatched?.Invoke(Ip, args);
                }
            });
            t1.Start();
        }

        static void Program_AggregateExceptionCatched(object sender, AggregateExceptionArgs e)
        {
            foreach (var item in e.AggregateException.InnerExceptions)
            {
                //Console.WriteLine("异常类型：{0}{1}来自：{2}{3}异常内容：{4},IP:{5}",
                //    item.GetType(), Environment.NewLine, item.Source,
                //    Environment.NewLine, item.Message,sender.ToString());
                logger.Error("wmi service connect error:{0}\t {1} \t {2},IP:{3}",item.GetType(),item.Source,item.Message,sender.ToString());
            }
            
        }


        public JArray GetLogicalDisk()
        {
            return GetInfoFromWMI("Win32_LogicalDisk", "DeviceID,Size,FreeSpace");
        }

        public  JArray GetNAConfigurationInfo()
        {
            return GetInfoFromWMI("Win32_NetworkAdapterConfiguration",
                "IPAddress,DefaultIPGateway,DNSServerSearchOrder,IPSubnet,MACAddress");
        }


        public JArray GetProcessorInfo()
        {
            return GetInfoFromWMI("Win32_Processor",
                "Name,Family,L2CacheSize,Manufacturer,MaxClockSpeed,ProcessorId,LoadPercentage");
        }

        public JArray GetPhysicalMemoryInfo()
        {
            JArray jArrayAvailable = GetInfoFromWMI("Win32_PerfRawData_PerfOS_Memory",
                "AvailableMBytes");
            JArray jArrayTotal = GetInfoFromWMI("Win32_LogicalMemoryConfiguration",
                "TotalPhysicalMemory");
            JArray result=new JArray();
            result.Add(jArrayAvailable);
            result.Add(jArrayTotal);
            return result;
        } 
        /// <summary>
        /// 通过wmi查找相关信息
        /// </summary>
        /// <param name="name">系统配置的名称</param>
        /// <param name="fields">需要查询的字段,多个字段用","分隔</param>
        /// <returns></returns>
        public JArray GetInfoFromWMI(String name, String fields)
        {
            JArray jarray = new JArray();
            try
            {
                ManagementObjectCollection mocObj =
                    new ManagementObjectSearcher(Ms, 
                        new ObjectQuery(       
                            String.Format("Select {0} From {1}", fields, name))).
                    Get();
                JObject jObj;
                foreach (ManagementObject mo in mocObj)
                {
                    jObj = new JObject();
                    foreach (String fieldName in fields.Split(','))
                    {
                        if (mo[fieldName] != null)
                            jObj.Add(new JProperty(fieldName, mo[fieldName]));
                    }
                    jarray.Add(jObj);
                }
            }
            catch
            {
                jarray = null;
            }
            return jarray;
        }


        public void processDeviceBeanData(DeviceBean deviceBean)
        {
            if (processStatus == 1) return;//当前数据处理操作标志为1时不进行额外的数据处理线程发起
            Task task = new Task(() => {
                try
                {
                    processStatus = 1; //将数据处理标志置为处理中
                    string cpuInfo = getCpuInfo();
                    long[] memoryInfo = getMemoryUsage();
                    long[] diskInfo = getDiskUsage();
                    if (string.IsNullOrWhiteSpace(cpuInfo) || memoryInfo[0] <= 0 || diskInfo[0] <= 0)
                    {
                        //deviceBean.IsConnected = false;
                    }
                    else
                    {
                        //deviceBean.IsConnected = true;
                        deviceBean.ResultCpuLoad = int.Parse(cpuInfo);
                        deviceBean.ReslutTotalMemory = memoryInfo[0];
                        deviceBean.ResultAvailableMemory = memoryInfo[1];
                        deviceBean.ResultMemoryUsage = (int)memoryInfo[2];
                        deviceBean.ResultTotalDiskSpace = diskInfo[0];
                        deviceBean.ResultFreeDiskSpace = diskInfo[1];
                        deviceBean.ResultDiskUsage = (int)diskInfo[2];
                    }
                    deviceBean.UpdateTime = DateTime.Now.ToString();
                    processStatus = 0; //将数据处理标志置为初始状态
                }
                catch (Exception e)
                {
                    processStatus = 0; //将数据处理标志置为初始状态
                    logger.Error("error while process device data.server ip {0},message: {1}", ip, e.Message.ToString());
                }
            });
            
        }

        #region WMI方式获取CPU信息
        public string getCpuInfo()
        {
            string result = "";
            ManagementObjectCollection moc = getQueryResult("select * from Win32_Processor");
            if (moc != null)
            {
                string[] list = new string[moc.Count];
                int i = 0;
                foreach (ManagementObject mo in moc)
                {
                    string total = mo.GetPropertyValue("LoadPercentage").ToString();
                    list[i] = total;
                    i++;
                }
                result = list[0].ToString();
            }
            else {
                //do nothing
            }
            return result;
        }
        #endregion

        #region WMI方式获取内存使用率
        public long[] getMemoryUsage()
        {
            //结果数组中0位为系统内存总量，1位为系统可用内存量，2位为内存占用百分比
            long[] result = new long[3];
            long totalm = 0;
            long avilablem = 0;
            long memoryusage = 0;
            ManagementObjectCollection mcr = getQueryResult("select * from Win32_ComputerSystem");
            if (mcr != null)
            {
                foreach (ManagementObject mo in mcr)
                {
                    string total = mo.GetPropertyValue("TotalPhysicalMemory").ToString();
                    totalm += long.Parse(total);
                }
            }
            else {
                //do nothing
            }
            ManagementObjectCollection moc = getQueryResult("select * from Win32_PerfRawData_PerfOS_Memory");
            if (moc != null)
            {
                foreach (ManagementObject mo in moc)
                {
                    string avilable = mo.GetPropertyValue("AvailableBytes").ToString();
                    avilablem = long.Parse(avilable);
                    break;
                }
            }
            else {
                //do nothing
            }
            
            long usedm = totalm - avilablem;
            if (totalm > 0)
            {
                memoryusage = (usedm * 100 )/ totalm;
            }
            result[0] = totalm; result[1] = avilablem; result[2] = memoryusage;
            return result;
        }
        #endregion

        #region WMI方式获取硬盘使用率
        public long[] getDiskUsage()
        {
            //结果数组中0位为系统内存总量，1位为系统可用内存量，2位为内存占用百分比
            long[] result = new long[3];
            long totalSpace = 0;
            long freeSpace = 0;
            long diskUsage = 0;
            ManagementObjectCollection mcr = getQueryResult("select * from Win32_LogicalDisk");
            if (mcr != null)
            {
                foreach (ManagementObject mo in mcr)
                {
                    //磁盘容量查询时可能产生未分区空间Size与FreeSpace为null的情况
                    string total = mo.GetPropertyValue("Size") == null ? "0": mo.GetPropertyValue("Size").ToString();
                    string free = mo.GetPropertyValue("FreeSpace") == null ? "0" : mo.GetPropertyValue("FreeSpace").ToString();
                    totalSpace += long.Parse(total);
                    freeSpace += long.Parse(free);
                }
            }
            else
            {
                //do nothing
            }

            long diskUsed  = totalSpace - freeSpace;
            if (totalSpace > 0)
            {
                diskUsage = (diskUsed * 100)/ totalSpace;
            }
            result[0] = totalSpace; result[1] = freeSpace; result[2] = diskUsage;
            return result;
        }
        #endregion

        public ManagementObjectCollection getQueryResult(String sql)
        {
            ManagementObjectCollection moc = null;
            try
            {
                if (!ms.IsConnected && !localConnection)
                {
                    //ms.Connect(); //service中不再进行重连
                    //do nothing    
                }
                ManagementObjectSearcher mos = new ManagementObjectSearcher(Ms, new ObjectQuery(sql));
                moc = mos.Get();
            }
            catch (Exception)
            {
                logger.Info("connect remote wmiservice failed..ip:{}" , ip);
            }
                
            return moc;
        }


        private enum Unit { B, KB, MB, GB, ER }
        private string FormatBytes(double bytes)
        {
            int unit = 0;
            while (bytes > 1024)
            {
                bytes /= 1024;
                ++unit;
            }
            return string.Format("{0} {1}", 
                bytes.ToString("F"), 
                ((Unit)unit).ToString());
        }

        //定义异常参数处理
        public class AggregateExceptionArgs : EventArgs
        {
            public AggregateException AggregateException { get; set; }
        }

    }
}